// # 包装类型

/*
我们已经知道，Java的数据类型分两种：

基本类型：byte，short，int，long，boolean，float，double，char

引用类型：所有class和interface类型

引用类型可以赋值为null，表示空，但基本类型不能赋值为null：
*/
String s = null; //Ok,String是一个class
int n = null //Error,int是一个basic type

/*
那么，如何把一个基本类型视为对象（引用类型）？

比如，想要把int基本类型变成一个引用类型，我们可以定义一个Integer类，它只包含一个实例字段int，这样，Integer类就可以视为int的包装类（Wrapper Class）：
*/
public class Integer {
	private int value;

	public Integer(int value) {
		this.value = value;
	}

	public int intValue() {
		return this.value;
	}
}

//定义好了Integer类，我们就可以把int和Integer互相转换：

Integer n = null;
Integer n2 = new Integer(99);
int n3 = n2.intValue();

/*
实际上，因为包装类型非常有用，Java核心库为每种基本类型都提供了对应的包装类型：

基本类型	对应的引用类型
boolean	java.lang.Boolean
byte	java.lang.Byte
short	java.lang.Short
int	java.lang.Integer
long	java.lang.Long
float	java.lang.Float
double	java.lang.Double
char	java.lang.Character

*/

//我们可以直接使用，并不需要自己去定义：
public class Main {
	public static void main(String[] args) {
		int i = 100;

		//1.通过new操作符创建Integer实例(不推荐使用,会有编译警告)
		Integer n1 = new Integer(i);

		//2.通过静态方法valueOf(int)创建Integer实例
		Integer n2 = Integer.valueOf(i);

		//2.通过静态方法valueOf(String)创建Integer实例
		Integer n3 = Integer.valueOf("100");

		System.out.println(n3.intValue());
	}
}



// Auto Boxing
//因为int喝nteger可以互相转换:
int i = 100;
Integer n = Integer.valueOf(i);

int x = n.intValue();


//所以,Java编译器可以帮助我们自动在int和Integer之间转型:
Integer n = 100; //编译器自动使用Integer.valueOf(int)
int x = n; //编译器自动使用Integer.intValue()


/*
这种直接把int变为Integer的赋值写法，称为‘自动装箱（Auto Boxing）’，反过来，把Integer变为int的赋值写法，称为自动拆箱（Auto Unboxing）。

注意：【自动装箱】和【自动拆箱】只发生在‘编译阶段’，目的是为了少写代码。

‘装箱’和‘拆箱’会影响【代码的执行效率】，因为‘编译后的class代码’是严格区分‘基本类型’和‘引用类型’的。并且，自动拆箱执行时可能会报 NullPointerException：

*/

// NullPointerException
public class Main {
	public static void main(String[] args) {
		Integer n = null; 
		int i = n; //NullPointerException
	}
}



// 不变类
//所有的 包装类型(Wrapper Class)都是不变类。我们查看Integer的Source Code可知，它d的核心代码如下
public final class Integer {
	private final int value;
}

//因此，一旦创建了Integer对象，该对象就是不变的。

//对两个Integer实例进行比较要特别注意：绝对不能用==比较，因为Integer是引用类型，必须使用equals()比较：

// '==' or 'equals'
public class Main {
	public static void main(String[] args) {
		Integer x = 127;//=Integer.valueOf(int)
		Integer y = 127;
		Integer m = 99999;
		Integer n = 99999;

		System.out.println("x == y: " + (x==y));//true
		System.out.println("m == n: " + (m==n)); //false
	}
}

/*
仔细观察结果的童鞋可以发现，==比较，
	较小的两个相同的Integer返回true;
	较大的两个相同的Integer返回false;
	这是因为Integer是不变类，

	编译器把Integer x = 127;自动变为Integer x = Integer.valueOf(127);

	为了节省内存，Integer.valueOf()对于较小的数，始终返回相同的实例，因此，==比较“恰好”为true，

	但我们绝不能因为Java标准库的Integer内部有缓存优化就用==比较，必须用equals()方法比较两个Integer。

!!! 按照语义编程，而不是针对特定的底层实现去“优化”。


因为Integer.valueOf()可能始终返回同一个Integer实例，因此，在我们自己创建Integer的时候，以下两种方法：
*/

//方法1：new instance
Integer n = new Integer(100);
	
//方法2：static factory method(From Cache)
Integer n = Integer.valueOf(100);

/*
方法2更好，因为方法1总是创建新的Integer实例，方法2把内部优化留给Integer的实现者去做，即使在当前版本没有优化，也有可能在下一个版本进行优化
(ps：常量池优化)

我们把能【创建“新”对象的静态方法】称为【静态工厂方法】。

Integer.valueOf()就是【静态工厂方法】，它尽可能地返回缓存的实例以节省内存。

--创建新对象的时候，优先选用【静态工厂方法】，而不是new操作符--

如果我们考察Byte.valueOf()方法的源码，可以看到，标准库 返回 Byte实例 全部是 缓存实例，但调用者并不关心静态工厂方法以何种方式创建新实例还是直接返回缓存的实例。


*/



// 进制转换
//Integer类,本身还提供了【大量方法】。
//例如：最常用的静态方法parseInt()可以把String解析成一个Int
int x1 = Integer.parseInt("100"); // 100
int x2 = Integer.parseInt("100",16); //256,16进制


//Integer还可以把‘整数’格式化为‘指定进制的字符串’：
public class Main {
	public static void main(String[] args) {
		System.out.println(Integer.toString(100)); // "100",default=10

		System.out.println(Integer.toString(100,36)); //"2s",36进制

		System.out.println(Integer.toHexString(100)); //"64",16进制

		System.out.println(Integer.toOctalString(100)); //"144",8进制

		System.out.println(Integer.toBinaryString(100)); //"1100100",表示为2进制
	}
}

/*注意：上述方法的输出都是String，在计算机内存中，只用二进制表示，不存在十进制或十六进制的表示方法。int n = 100在内存中总是以4字节的二进制表示：

	┌────────┬────────┬────────┬────────┐
	│00000000│00000000│00000000│01100100│
	└────────┴────────┴────────┴────────┘

我们经常使用的System.out.println(n);是依靠核心库自动把整数格式化为10进制输出并显示在屏幕上，使用Integer.toHexString(n)则通过核心库自动把整数格式化为16进制。


这里我们还要注意:程序设计的一个重要原则--数据存储 + 显示分离

‘Java的包装类型’还定义了一些有用的【静态变量】


*/

//boolean只有2个值 true/false,其包装类型只需要引用Boolean提供的static field(静态字段):
Boolean t = Boolean.True;
Boolean f = Boolean.FALSE;

//int可表示の最大值/最小值
int max = Integer.MAX_VALUE; //2147483647
int min = Integer.MIN_VALUE; //-2147483648

//long类型占用的bit和byte数量:
int sizeOfLong = Long.SIZE; //64 (bits)
int bytesOfLong = Long.BYTES; //8 (bytes)



//最后，所有的整数和浮点数的包装类型都继承自Number，因此，可以非常方便地直接通过包装类型获取各种基本类型：

//向上转型为Number:
Number num = new Integer(999);

//获取byte,int,long,float,double
byte b = num.byteValue();
int n = num.intValue();
long ln = num.longValue();
float f = num.floatValue();
double d = num.doubleValue();



// # 处理无符号整型
/*
在Java中，并没有无符号整型（Unsigned）的基本数据类型。

byte、short、int和long都是带符号整型，最高位是符号位。

而C语言则提供了CPU支持的全部数据类型，包括无符号整型。

‘无符号整型’和‘有符号整型’的【转换】在Java中就需要借助【包装类型的静态方法】完成。

例如，byte是有符号整型，范围是-128~+127，但如果把byte看作无符号整型，它的范围就是0~255。

我们把一个负的byte按无符号整型转换为int：
*/
public class Main {
	public static void main(String[] args) {
		byte x = -1;
		byte y = 127;
		System.out.println(Byte.toUnsignedInt(x)); //255
		System.out.println(Byte.toUnsignedInt(y)); //127
	}
}

/*
因为byte的-1的二进制表示是11111111，以无符号整型转换后的int就是255。

类似的，可以把一个short按unsigned转换为int，把一个int按unsigned转换为long。

*/





/* 小结

1.Java核心库提供的 包装类型(Wrapper Class)可以把 '基本类型' 包装为class；

2.[自动装箱]和[自动拆箱]都是在编译期完成的（JDK>=1.5）；

3.[装箱]和[拆箱]会影响执行效率，且拆箱时可能发生NullPointerException；

4.【包装类型】的‘比较’必须使用equals()；

5.【整数和浮点数的包装类型】都继承自Number；

6.【包装类型】提供了大量实用方法。

*/




























































